﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


using RecommenderSystem.misc;
//using log4net;

namespace RecommenderSystem.algorithms
{
    public class Pearson: MemoryBased
    {
        //Caching users similarity
        private Cache similarityCache;
        public readonly string ALGORITHM_NAME = "Pearson";
        public Pearson(Dictionary<string, User> users, ICollection<IDataRecord> dataset) : base(users, dataset) 
        {
            similarityCache = new Cache();
        }

        public Pearson(Dictionary<string, User> users, ICollection<IDataRecord> dataset, Dictionary<string, Cache> cache)
            : base(users, dataset)
        {
            if (!cache.ContainsKey(ALGORITHM_NAME))
                cache.Add(ALGORITHM_NAME, new Cache());

            similarityCache = cache[ALGORITHM_NAME];
        }

        private void initUsersDataSet(ICollection<IDataRecord> dataSet)
        {
            foreach (IDataRecord record in dataSet)
            {
                string userId = record.getData(Config.USER_ID);
                string itemId = record.getData(Config.ITEM_ID);
                string itemRate = record.getData(Config.ITEM_RANK);
                User user = null;
                if (_users.ContainsKey(userId))//Already added
                {
                    user = _users[userId];
                }
                else//First time
                {
                    user = new User(userId);
                    _users.Add(userId, user);
                }
                user.addItem(itemId, itemRate);
            }
//            if (logger.IsDebugEnabled)
//            {
//                logger.Debug("Loaded " + _users.Keys.Count + " Users.");
//            }
            
        }

        public override double calculateSimilarity(User user, User otherUser)
        {
            if (user == null | otherUser == null)
            {
//                logger.Warn("Failed calculate similarity, reason: null pointer");
                return -1d;
            }

            if (similarityCache.isCached(user.getUserId(), otherUser.getUserId()))
            {
                return similarityCache.retrieveFromCache(user.getUserId(), otherUser.getUserId());
            }

            IEnumerable<string> sameRatedItemsIds = user.getRatedItemsIds().Intersect(otherUser.getRatedItemsIds());
            double numerator = 0.0;
            double denominator = 0.0;
            double tmpA = 0.0;
            double tmpB = 0.0;
            double tmpC = 0.0;
            double tmpD = 0.0;
            foreach (string itemId in sameRatedItemsIds)
            {
                tmpA = user.getItemRate(itemId) - user.getAverageRating();
                tmpB = otherUser.getItemRate(itemId) - otherUser.getAverageRating();
                numerator += Math.Round(tmpA * tmpB, 4);
                tmpC += Math.Round(Math.Pow(tmpA, 2), 4);
                tmpD += Math.Round(Math.Pow(tmpB, 2), 4);
            }
            denominator = Math.Round(Math.Sqrt(tmpC) * Math.Sqrt(tmpD), 4);

            double usersSimilarity = denominator != 0 ? numerator / denominator : 0d;

            similarityCache.addToCache(user.getUserId(), otherUser.getUserId(), usersSimilarity);
            similarityCache.addToCache(otherUser.getUserId(), user.getUserId(), usersSimilarity);
            
            return usersSimilarity; 
        }


    
    }
}
